﻿using ApiConceptsFramework;
using ApiConceptsFramework.Concepts;
using ApiConceptsFramework.Contract;
using ApiConceptsFramework.HCs;
using FluentAssertions;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace test.ApiConceptsFramework.Pattern
{
	static class TestClassExtensions
	{
		//to test that extension methods are not rated as factory methods
		public static FactoryTests.TestClass TestClassExtensionMethod(this FactoryTests.TestClass t) { return t; }
	}

	[TestFixture]
	public class FactoryTests
	{
// ReSharper disable UnusedMember.Local
		public class TestClass
		{
			public TestClass NonStaticMethod() { return null; }
			public static void StaticVoidMethod() { }
			public static TestClass StaticFactoryMethod1() { return null; }
			public static TestClass StaticFactoryMethod2() { return null; }
		}

		
// ReSharper restore UnusedMember.Local

		[Test]
		public void No_factory_empty_list()
		{
			//Arrange
			var apiAssemblies = new List<Assembly> {GetType().Assembly};
			var factoryPattern = new Factory();
			var data = new ConceptInstance[] {};

			//Act
			var result = factoryPattern.Evaluate(apiAssemblies, data, null, null);

			//Assert
			result.Should().BeNull();
		}

		[Test]
		public void No_factory_only_other_concepts()
		{
			//Arrange
			var apiAssemblies = new List<Assembly> { GetType().Assembly };
			var factoryPattern = new Factory();
			var data = new []
				{
					new ConceptInstance {Concept = new ClassUsage()},
					new ConceptInstance {Concept = new Instantiation()},
					new ConceptInstance {Concept = new FieldAccess()},
					new ConceptInstance //non-static methods are no factory methods
						{
							Concept = new MethodCall(),
							NumberOfUsages = 1,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClass).GetMethod("NonStaticMethod")}
						},
					new ConceptInstance //static void methods are no factory methods
						{
							Concept = new MethodCall(),
							NumberOfUsages = 1,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClass).GetMethod("StaticVoidMethod")}
						},
					new ConceptInstance //extension methods are no factory methods
						{
							Concept = new MethodCall(),
							NumberOfUsages = 1,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClassExtensions).GetMethod("TestClassExtensionMethod")}
						},
				};

			//Act
			var result = factoryPattern.Evaluate(apiAssemblies, data, null, null);

			//Assert
			result.Should().BeNull();
			data.SelectMany(d => d.HcData).Count().Should().Be(0);
		}

		[Test]
		public void Factory_2_methods_with_1_usage_each()
		{
			//Arrange
			var apiAssemblies = new List<Assembly> { GetType().Assembly };
			var factoryPattern = new Factory();
			var data = new []
				{
					new ConceptInstance
						{
							Concept = new MethodCall(),
							NumberOfUsages = 1,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClass).GetMethod("StaticFactoryMethod1")}
						},
					new ConceptInstance
						{
							Concept = new MethodCall(),
							NumberOfUsages = 1,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClass).GetMethod("StaticFactoryMethod2")}
						}
				};

			//Act
			var result = factoryPattern.Evaluate(apiAssemblies, data, null, null);

			//Assert
			result.Should().NotBeNull();
			result.HighLevelConcept.Should().Be(factoryPattern);
			result.GeneralSuggestions.Should().NotBeEmpty();
			data[0].HcData.Should().HaveCount(1);
			data[0].HcData[0].HighLevelConcept.Should().Be(factoryPattern);
			data[0].HcData[0].UsageComplexity.Should().Be(0); //no additional costs, the pattern data is just for the description!
			data[1].HcData.Should().HaveCount(1);
			data[1].HcData[0].HighLevelConcept.Should().Be(factoryPattern);
			data[1].HcData[0].UsageComplexity.Should().Be(0);
			Console.WriteLine(result.GeneralSuggestions[0]);
		}

		[Test]
		public void Factory_1_method_with_2_usages()
		{
			//Arrange
			var apiAssemblies = new List<Assembly> { GetType().Assembly };
			var factoryPattern = new Factory();
			var data = new[]
				{
					new ConceptInstance
						{
							Concept = new MethodCall(),
							NumberOfUsages = 2,
							Target = new MeasurableTarget{Type = typeof(TestClass), Member = typeof(TestClass).GetMethod("StaticFactoryMethod1")}
						},
				};

			//Act
			var result = factoryPattern.Evaluate(apiAssemblies, data, null, null);

			//Assert
			result.Should().NotBeNull();
			result.GeneralSuggestions.Should().NotBeEmpty();
			data[0].HcData.Should().HaveCount(2);
			data[0].HcData.Sum(p => p.UsageComplexity).Should().Be(0);  //no additional costs, the pattern data is just for the description!
			Console.WriteLine(result.GeneralSuggestions[0]);
		}

		[Test]
		public void Factory_has_no_hcInputData()
		{
			var factoryPattern = new Factory();
			factoryPattern.InputDataType.Should().Be(null);
		}
	}
}
